package com.ansorgit.helpful.smartSplit;

import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;

/**
 * This editor handler splits a string literal into two parts seperated by + operators.
 * It works with JavaScript and Java literals.
 * <p/>
 * Position the caret inside a string literal and press alt+PLUS, a string "ab|c" is turned
 * into "ab" + | + "c".
 * <p/>
 * User: jansorg
 * Date: Mar 17, 2010
 * Time: 9:26:14 PM
 */
public class SmartSplitHandler extends EditorActionHandler {
    @Override
    public void execute(final Editor editor, DataContext dataContext) {
        final DocumentEx doc = (DocumentEx) editor.getDocument();
        final Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(editor.getContentComponent()));

        final PsiDocumentManager docManager = PsiDocumentManager.getInstance(project);
        PsiFile psiFile = docManager.getPsiFile(doc);
        if (psiFile == null) {
            return;
        }

        docManager.commitDocument(doc);

        final int currentOffset = editor.getCaretModel().getOffset();
        PsiElement element = psiFile.findElementAt(currentOffset);
        if (element == null) {
            return;
        }

        final boolean hasSelection = editor.getSelectionModel().hasSelection();

        if (element instanceof PsiJavaToken && ((PsiJavaToken) element).getTokenType() == JavaTokenType.STRING_LITERAL) {
            ApplicationManager.getApplication().runWriteAction(new Computable<Object>() {
                public Object compute() {
                    if (hasSelection) {
                        splitAtSelection(editor, "\" + \"", "\" + \"");
                    } else {
                        splitAt(editor, currentOffset, "\" + ", null, " + \"");
                    }
                    return null;
                }
            });
        } else if ("PsiElement(JS:STRING_LITERAL)".equals(element.toString()) || "PsiElement(JS:SINGLE_QUOTE_STRING_LITERAL)".equals(element.toString())) {
            final boolean singleQuotes = element.getText().startsWith("'");
            ApplicationManager.getApplication().runWriteAction(new Computable<Object>() {
                public Object compute() {
                    if (hasSelection) {
                        splitAtSelection(editor, singleQuotes ? "' + '" : "\" + \"", singleQuotes ? "' + '" : "\" + \"");
                    } else {
                        String prefix = singleQuotes ? "' + " : "\" + ";
                        String suffix = singleQuotes ? " + '" : " + \"";
                        splitAt(editor, currentOffset, prefix, null, suffix);
                    }
                    return null;
                }
            });
        }
    }

    private int splitAtSelection(Editor editor, String prefix, String suffix) {
        String text = editor.getSelectionModel().getSelectedText();
        int start = editor.getSelectionModel().getSelectionStart();
        int end = editor.getSelectionModel().getSelectionEnd();

        editor.getDocument().replaceString(start, end, prefix + text + suffix);

        int newOffset = start + prefix.length();

        editor.getSelectionModel().removeSelection();
        editor.getCaretModel().moveToOffset(newOffset);

        return newOffset;
    }

    private static int splitAt(Editor editor, int startOffset, String prefix, String middle, String suffix) {
        editor.getDocument().insertString(startOffset, suffix);

        if (middle != null) {
            editor.getDocument().insertString(startOffset, middle);
        }

        editor.getDocument().insertString(startOffset, prefix);
        editor.getCaretModel().moveToOffset(startOffset + prefix.length());

        return startOffset + prefix.length();
    }
}
